package com.gitee.dupe.dict.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.gitee.dolt.boot.service.SuperCacheServiceImpl;
import com.gitee.dolt.cache.repository.CachePlusOps;
import com.gitee.dolt.core.cache.model.CacheHashKey;
import com.gitee.dolt.core.cache.model.CacheKey;
import com.gitee.dolt.core.util.BizAssert;
import com.gitee.dolt.injection.properties.InjectionProperties;
import com.gitee.dupe.common.cache.CacheKeyDefinition;
import com.gitee.dupe.common.cache.DictionaryTypeCacheKeyBuilder;
import com.gitee.dupe.dict.dao.DictionaryMapper;
import com.gitee.dupe.dict.dto.DictionarySaveDTO;
import com.gitee.dupe.dict.dto.DictionaryTypeSaveDTO;
import com.gitee.dupe.dict.entity.Dictionary;
import com.gitee.dupe.dict.service.IDictionaryService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author LYH
 * @since 2020-10-11
 */
@Service
@RequiredArgsConstructor
public class DictionaryServiceImpl extends SuperCacheServiceImpl<DictionaryMapper, Dictionary> implements IDictionaryService {

    private final InjectionProperties ips;
    private final CachePlusOps cachePlusOps;
    private final DictionaryTypeCacheKeyBuilder dictionaryTypeCacheKeyBuilder = new DictionaryTypeCacheKeyBuilder();

    @Override
    protected String prefix() {
        return CacheKeyDefinition.DICTIONARY;
    }

    @Override
    public Dictionary handlerSave(DictionarySaveDTO model) {
        Dictionary dictionary = BeanUtil.toBean(model, Dictionary.class);
        save(dictionary);
        return dictionary;
    }

    @Override
    public Dictionary handlerUpdate(DictionarySaveDTO model) {
        Dictionary type = getById(model.getParentId());
        BizAssert.isTrue(type != null, StrUtil.format("字典父级id[{}]不存在", model.getParentId()));
        Dictionary dictionary = BeanUtil.toBean(model, Dictionary.class);
        dictionary.setType(type.getType());
        updateById(dictionary);
        return dictionary;
    }

    @Override
    public Dictionary saveType(DictionaryTypeSaveDTO dictType) {
        int typeCount = count(Wrappers.<Dictionary>lambdaQuery().eq(Dictionary::getType, dictType.getType()));
        BizAssert.isTrue(typeCount == 0, StrUtil.format("字典类型[{}]已存在", dictType.getType()));
        Dictionary dict = BeanUtil.toBean(dictType, Dictionary.class);
        super.save(dict);
        return dict;
    }

    @Override
    public Dictionary updateType(DictionaryTypeSaveDTO dictType) {
        int typeCount = count(Wrappers.<Dictionary>lambdaQuery().isNull(Dictionary::getParentId)
                .eq(Dictionary::getType, dictType.getType())
                .ne(Dictionary::getId, dictType.getId()));
        BizAssert.isTrue(typeCount == 0, StrUtil.format("字典类型[{}]已存在", dictType.getType()));
        Dictionary dictionaryDb = getById(dictType.getId());
        List<Dictionary> items = list(Wrappers.<Dictionary>lambdaQuery().eq(Dictionary::getParentId, dictionaryDb.getId()));

        items.forEach(item -> item.setType(dictType.getType()));
        updateBatchById(items);

        delCache(items.stream().map(Dictionary::getId).collect(toList()));
        delCache(dictType.getId());
        CacheKey typeKey = dictionaryTypeCacheKeyBuilder.key(dictType.getType());
        cachePlusOps.del(typeKey);

        Dictionary type = BeanUtil.toBean(dictType, Dictionary.class);
        super.updateById(type);
        return type;
    }

    @Override
    public boolean deleteType(List<Long> ids) {
        List<Dictionary> types = list(Wrappers.<Dictionary>lambdaQuery().in(Dictionary::getId, ids));
        List<Dictionary> items = list(Wrappers.<Dictionary>lambdaQuery().in(Dictionary::getParentId, ids));
        List<Long> itemIds = items.stream().map(Dictionary::getId).collect(toList());
        boolean bool = removeByIds(itemIds) && super.removeByIds(ids);
        delCache(itemIds);
        delCache(ids);
        types.forEach(type -> {
            CacheKey typeKey = dictionaryTypeCacheKeyBuilder.key(type.getType());
            cachePlusOps.del(typeKey);
        });
        return bool;
    }

    @Override
    public boolean save(Dictionary model) {
        Dictionary type = getById(model.getParentId());
        BizAssert.isTrue(type != null, StrUtil.format("字典父级id[{}]不存在", model.getParentId()));
        model.setType(type.getType());
        int count = count(Wrappers.<Dictionary>lambdaQuery().eq(Dictionary::getParentId, model.getParentId())
                .eq(Dictionary::getKey, model.getKey()));
        BizAssert.isTrue(count == 0, StrUtil.format("字典[{}]已存在", model.getKey()));
        boolean bool = super.save(model);
        CacheHashKey typeKey = dictionaryTypeCacheKeyBuilder.hashKey(model.getKey(), model.getType());
        cachePlusOps.hSet(typeKey, model.getId());
        return bool;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateAllById(Dictionary model) {
        return update(model, super::updateAllById);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateById(Dictionary model) {
        return update(model, super::updateById);
    }

    private boolean update(Dictionary model, Function<Dictionary, Boolean> function) {
        int count = count(Wrappers.<Dictionary>lambdaQuery().eq(Dictionary::getParentId, model.getParentId())
                .eq(Dictionary::getKey, model.getKey()).ne(Dictionary::getId, model.getId()));
        BizAssert.isTrue(count == 0, StrUtil.format("字典[{}]已存在", model.getKey()));
        Dictionary old = getById(model.getId());
        BizAssert.isTrue(old != null, "字典不存在或已被删除");
        boolean bool = function.apply(model);

        CacheHashKey typeKey = dictionaryTypeCacheKeyBuilder.hashKey(old.getKey(), old.getType());
        cachePlusOps.hDel(typeKey);
        CacheHashKey newKey = dictionaryTypeCacheKeyBuilder.hashKey(old.getKey(), old.getType());
        cachePlusOps.hSet(newKey, old.getId());
        return bool;
    }


    @Override
    public boolean removeById(Serializable id) {
        Dictionary model = getById(id);
        BizAssert.isTrue(model != null, "字典项不存在");
        boolean remove = super.removeById(id);

        CacheHashKey typeKey = dictionaryTypeCacheKeyBuilder.hashKey(model.getKey(), model.getType());
        cachePlusOps.hDel(typeKey);
        return remove;
    }

    @Override
    public boolean removeByIds(Collection<? extends Serializable> idList) {
        if (idList.isEmpty()) {
            return true;
        }
        List<Dictionary> list = listByIds(idList);
        boolean remove = super.removeByIds(idList);
        list.forEach(model -> {
            CacheHashKey typeKey = dictionaryTypeCacheKeyBuilder.hashKey(model.getKey(), model.getType());
            cachePlusOps.hDel(typeKey);
        });
        return remove;
    }

    @Override
    public Map<String, List<Dictionary>> listByTypes(List<String> types) {
        if (ArrayUtil.isEmpty(types)) {
            return Collections.emptyMap();
        }
        List<Dictionary> list = baseMapper.findAllByTypesAndEnabledTrue(types);

        //key 是类型
        return list.stream().sorted(Comparator.comparing(l -> l.getSort(), Comparator.nullsLast(Integer::compareTo))).collect(groupingBy(Dictionary::getType, LinkedHashMap::new, toList()));
    }

    @Override
    public Map<Serializable, Object> findDictionaryItem(Set<Serializable> typeKeys) {
        if (typeKeys.isEmpty()) {
            return Collections.emptyMap();
        }

        Set<String> types = new HashSet<>();
        Set<String> multiCodes = new HashSet<>();
        typeKeys.stream().filter(Objects::nonNull).forEach(item -> {
            String[] temp = StrUtil.split(String.valueOf(item), ips.getDictSeparator());
            types.add(temp[0]);
            if (temp.length > 1) {
                multiCodes.add(temp[1]);
            }
        });
        Set<String> keys = multiCodes.parallelStream()
                .map(item -> StrUtil.split(String.valueOf(item), ips.getDictItemSeparator()))
                .flatMap(Arrays::stream).collect(Collectors.toSet());

        // 1.1 根据 type+code 查询 item id
        Set<Long> itemIdList = new HashSet<>();
        for (String type : types) {
            for (String key : keys) {
                String itemId = cachePlusOps.hGet(dictionaryTypeCacheKeyBuilder.hashKey(key, type), hk -> {
                    Dictionary one = getOne(Wrappers.<Dictionary>lambdaQuery().eq(Dictionary::getType, type).eq(Dictionary::getKey, key), false);
                    return one != null ? String.valueOf(one.getId()) : null;
                }, true);
                if (itemId != null) {
                    itemIdList.add(Convert.toLong(itemId));
                }
            }
        }
        // 1.2 根据 item id 查询DictionaryItem
        List<Dictionary> list = findByIds(itemIdList, this::listByIds);

        // 2. 将 list 转换成 Map，Map的key是字典编码，value是字典名称
        Map<Serializable, Object> typeMap = list.stream().collect(Collectors.toMap(item ->
                        StrUtil.join(ips.getDictSeparator(), item.getType(), item.getKey()),
                item -> item.getLabel()));

        return typeMap;
    }

    @Override
    public void refreshCache() {
        List<Dictionary> list = list();
        list.forEach(model -> {
            this.setCache(model, false);
            if (StrUtil.isAllNotEmpty(model.getKey(), model.getType())) {
                CacheHashKey typeKey = dictionaryTypeCacheKeyBuilder.hashKey(model.getKey(), model.getType());
                cachePlusOps.hSet(typeKey, model.getId());
            }
        });
    }


}
